﻿using System;
using System.Collections.Generic;
using System.Drawing;
using Greenshot2Core.Unmanaged;
using System.Runtime.InteropServices;
using System.Windows.Media.Imaging;
using System.Windows.Interop;
using System.Windows;

namespace Greenshot2Core.Capture {
	public static class CaptureScreen {
		private static NLog.Logger LOG = NLog.LogManager.GetCurrentClassLogger();

		/// <summary>
		/// Helper method to create an exception that might explain what is wrong while capturing
		/// </summary>
		/// <param name="method">string with current method</param>
		/// <param name="capture">ICapture</param>
		/// <param name="captureBounds">Rectangle of what we want to capture</param>
		/// <returns></returns>
		private static Exception CreateCaptureException(string method, Int32Rect captureBounds) {
			Exception exceptionToThrow = User32.CreateWin32Exception(method);
			if (!captureBounds.IsEmpty) {
				exceptionToThrow.Data.Add("Height", captureBounds.Height);
				exceptionToThrow.Data.Add("Width", captureBounds.Width);
			}
			return exceptionToThrow;
		}

		/// <summary>
		/// This method will use User32 / gdi32 code to capture the specified captureBounds from the screen
		/// </summary>
		/// <param name="captureBounds">Rectangle with the bounds to capture</param>
		/// <returns>BitmapSource which is captured from the screen at the location specified by the captureBounds</returns>
		public static BitmapSource CaptureRectangle(Int32Rect captureBounds) {
			BitmapSource returnBitmap = null;
			if (captureBounds.Height <= 0 || captureBounds.Width <= 0) {
				LOG.Warn("Nothing to capture, ignoring!");
				return null;
			} else {
				LOG.Debug("CaptureRectangle Called!");
			}
			using (SafeWindowDCHandle desktopDCHandle = SafeWindowDCHandle.fromDesktop()) {
				if (desktopDCHandle.IsInvalid) {
					// Get Exception before the error is lost
					Exception exceptionToThrow = CreateCaptureException("desktopDCHandle", captureBounds);
					// throw exception
					throw exceptionToThrow;
				}

				// create a device context we can copy to
				using (SafeCompatibleDCHandle safeCompatibleDCHandle = GDI32.CreateCompatibleDC(desktopDCHandle)) {
					// Check if the device context is there, if not throw an error with as much info as possible!
					if (safeCompatibleDCHandle.IsInvalid) {
						// Get Exception before the error is lost
						Exception exceptionToThrow = CreateCaptureException("CreateCompatibleDC", captureBounds);
						// throw exception
						throw exceptionToThrow;
					}
					// Create BitmapInfoHeader for CreateDIBSection
					BitmapInfoHeader bmi = new BitmapInfoHeader(captureBounds.Width, captureBounds.Height, 24);

					// Make sure the last error is set to 0
					Win32.SetLastError(0);

					// create a bitmap we can copy it to, using GetDeviceCaps to get the width/height
					IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap.
					using (SafeDibSectionHandle safeDibSectionHandle = GDI32.CreateDIBSection(desktopDCHandle, ref bmi, BitmapInfoHeader.DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0)) {
						if (safeDibSectionHandle.IsInvalid) {
							// Get Exception before the error is lost
							Exception exceptionToThrow = CreateCaptureException("CreateDIBSection", captureBounds);
							exceptionToThrow.Data.Add("hdcDest", safeCompatibleDCHandle.DangerousGetHandle().ToInt32());
							exceptionToThrow.Data.Add("hdcSrc", desktopDCHandle.DangerousGetHandle().ToInt32());

							// Throw so people can report the problem
							throw exceptionToThrow;
						} else {
							// select the bitmap object and store the old handle
							using (SafeSelectObjectHandle selectObject = safeCompatibleDCHandle.SelectObject(safeDibSectionHandle)) {
								// bitblt over (make copy)
								GDI32.BitBlt(safeCompatibleDCHandle, 0, 0, captureBounds.Width, captureBounds.Height, desktopDCHandle, captureBounds.X, captureBounds.Y, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
							}
							returnBitmap = Imaging.CreateBitmapSourceFromHBitmap(safeDibSectionHandle.DangerousGetHandle(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
						}
					}
				}
			}
			return returnBitmap;
		}

	}
}
